package ch.holzi.paperracers.view;

import java.util.LinkedList;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.Log;

public class Track 
{
	enum Mode
	{
		CREATING,
		CREATED
	}
	
	private LinkedList<TrackSegment> mRawTrack, mOuterBoundary;
	
	
	private Paint p;
	
	public static final String LOG_TAG = "Track";
	
	public Track()
	{
		mRawTrack = new LinkedList<TrackSegment>();
		mOuterBoundary = new LinkedList<TrackSegment>();
		
		  p = new Paint();
		  p.setColor(0xffff0000);
		  p.setStyle(Style.STROKE);
	}
	
	public void onDraw(Canvas c, Mode m)
	{
		if (m == Mode.CREATING)
		{
			if (!mRawTrack.isEmpty())
			{
				for (int i = 0; i < mRawTrack.size(); ++i)
				{
					mRawTrack.get(i).onDraw(c);
				}
			}
		}
		else if (m == Mode.CREATED)
		{
			if (!mRawTrack.isEmpty())
			{
				for (int i = 0; i < mRawTrack.size(); ++i)
				{
					mRawTrack.get(i).onDraw(c);
				}
				for (int i = 0; i < mOuterBoundary.size(); ++i)
				{
					mOuterBoundary.get(i).onDraw(c);
				}
			}
		}
	}
	
	public void reset()
	{
		mRawTrack.clear();
		mOuterBoundary.clear();
	}
	
	public void addSegment(TrackSegment s)
	{
		if (mRawTrack.size() != 0)
		{
			s.setPrevious(mRawTrack.getLast());
			s.setNext(mRawTrack.getFirst());
			s.setNeighborTrack(mRawTrack);

		}
		mRawTrack.add(s);
		mRawTrack.getLast().setColor(Color.BLACK, TrackSegment.Style.DOTTED);
		mRawTrack.getLast().setNext(s);
		mRawTrack.getFirst().setPrevious(mRawTrack.getLast());
		
		TrackSegment outer = new TrackSegment(s);
		outer.setNeighborTrack(mRawTrack);
		mOuterBoundary.add(outer);
		mOuterBoundary.getLast().setColor(Color.BLACK, TrackSegment.Style.FULL);
		mOuterBoundary.getLast().setNext(outer);
		mOuterBoundary.getFirst().setPrevious(mOuterBoundary.getLast());

	}
	
	public void setSegment(int i, float controlX, float controlY, float endX, float endY)
	{
		mRawTrack.get(i).setPath(controlX, controlY, endX, endY);
		mOuterBoundary.get(i).setPath(controlX, controlY, endX, endY);
	}
	
	/**
	 * Check's if the given point (x,y) is in the (internally) defined forbidden area. If that's the case then all
	 * previously added segments are also checked for that area and removed if needed
	 * @param x x-coordinate of concerned point
	 * @param y y-coordinate of concerned point
	 * @param x0 x-coordinate of start of the track. defines the forbidden area
	 * @param y0 y-coordinate of the start of the track. defines the forbidden area
	 * @param  x-coordinate of the last valid point of the track
	 * @param lastValidY y-coordinate of the last valid point of the track
	 * @return (x,y) of the last valid point of the track, or null if no segment was removed
	 */
	public PointF findLastValidSegment(float x, float y, float x0, float y0)
	{	
		PointF lastValidPoint = new PointF();
		boolean segmentsRemoved = false;
		
		float dx = Math.max(7*12, Math.abs(mRawTrack.get(0).getStartPoint().x-x0));
		float dy = Math.max(7*12, Math.abs(mRawTrack.get(0).getStartPoint().y-y0));
		// define the area where we don't want to have any segments!
		RectF forbiddenAreas[] = new RectF[5];
		forbiddenAreas[0] = new RectF(        0,         0,                   x0 - dx, dy);
		forbiddenAreas[1] = new RectF(x0 - dx,         0,                          x0, y0 - dy);
		forbiddenAreas[2] = new RectF(       x0,         0, GameView.getDisplaySize().x,        y0);
		forbiddenAreas[3] = new RectF(x0 - dx, y0 - dy,                          x0,        y0);
		forbiddenAreas[4] = new RectF(x0 - dx,        y0,                          x0, y0 + dy);
		
		if (isPointInArea(forbiddenAreas, new PointF(x, y)))
		{
			for (int i = mRawTrack.size()-1; i >= 0; i--)
			{
				if (isPointInArea(forbiddenAreas, mRawTrack.get(i).getEndPoint()))
				{
					lastValidPoint.x = mRawTrack.get(i-1).getEndPoint().x;
					lastValidPoint.y = mRawTrack.get(i-1).getEndPoint().y;
					mRawTrack.remove(i);
					mOuterBoundary.remove(i);
					segmentsRemoved = true;
				}
				else
				{
					break;
				}
			}
			if (segmentsRemoved)
			{
				return lastValidPoint;
			}
			else
			{
				return null;
			}
		}
		else
		{
			return null;
		}
	}
	
	public TrackSegment getSegment(int i)
	{
		return mRawTrack.get(i);
	}
	
	public int getSize()
	{
		return mRawTrack.size();
	}
	
	public void close()
	{
		// TODO: do sanity checks for length here
		
        // scale the outer and inner side of the track with respect of the boundary box center
        for (int i = 0; i < mRawTrack.size(); ++i)
        {
        	mOuterBoundary.get(i).scale(48);
        }
        
        int lastIndex = mOuterBoundary.indexOf(mOuterBoundary.getLast());

        
        // do first and last segment
        mOuterBoundary.getFirst().scale(mOuterBoundary.getLast(), mOuterBoundary.get(1));
        mOuterBoundary.getLast().scale(mOuterBoundary.get(lastIndex-1), mOuterBoundary.getFirst());
        // do all from 2nd to 2nd last segment
        for (int i = 1; i < mOuterBoundary.size()-1; ++i)
        {
        	mOuterBoundary.get(i).scale(mOuterBoundary.get(i-1), mOuterBoundary.get(i+1));
        }
         
        
        // do first and last segment
        mOuterBoundary.get(0).cleanup(mOuterBoundary.get(lastIndex), mOuterBoundary.get(1));
        mOuterBoundary.get(lastIndex).cleanup(mOuterBoundary.get(lastIndex-1), mOuterBoundary.get(0));
        // do all from 2nd to 2nd last segment
        for (int i = 1; i < mOuterBoundary.size()-1; ++i)
        {
        	mOuterBoundary.get(i).cleanup(mOuterBoundary.get(i-1), mOuterBoundary.get(i+1));
        }
        /*
        for (int i = 0; i < mOuterBoundary.size()-1; ++i)
        {
        	mOuterBoundary.get(i).setDistance();
        }
        
        for (int i = 0; i < mOuterBoundary.size()-1; ++i)
        {
        	mOuterBoundary.get(i).cleanup();
        }*/
        
        /*
        // do last segment
        mOuterBoundary.get(lastIndex).cleanup(mOuterBoundary.get(0));
        // do all from first to 2nd last segment
        for (int i = 0; i < mOuterBoundary.size()-1; ++i)
        {
        	mOuterBoundary.get(i).cleanup(mOuterBoundary.get(i+1));
        }
        
        
        mOuterBoundary.get(0).cleanup( mOuterBoundary.get(mOuterBoundary.size()-2), mOuterBoundary.get(mOuterBoundary.size()-1), mOuterBoundary.get(1), mOuterBoundary.get(2));
        mOuterBoundary.get(1).cleanup( mOuterBoundary.get(mOuterBoundary.size()-1), mOuterBoundary.get(0), mOuterBoundary.get(2), mOuterBoundary.get(3));
        
        mOuterBoundary.get(mOuterBoundary.size()-2).cleanup( mOuterBoundary.get(mOuterBoundary.size()-2-2), mOuterBoundary.get( mOuterBoundary.size()-2-1), mOuterBoundary.get( mOuterBoundary.size()-1), mOuterBoundary.get(0));
        mOuterBoundary.get(mOuterBoundary.size()-1).cleanup( mOuterBoundary.get(mOuterBoundary.size()-2-1), mOuterBoundary.get( mOuterBoundary.size()-2), mOuterBoundary.get(0), mOuterBoundary.get(1));
        
        
        for (int i = 2; i < mOuterBoundary.size()-2; ++i)
        {
        	mOuterBoundary.get(i).cleanup( mOuterBoundary.get(i-2), mOuterBoundary.get(i-1), mOuterBoundary.get(i+1), mOuterBoundary.get(i+1));
        }
        
        
        // do first and last segment
        mOuterBoundary.get(0).cleanup(mOuterBoundary.get(mOuterBoundary.size()-1), mOuterBoundary.get(1));
        mOuterBoundary.get(mOuterBoundary.size()-1).cleanup(mOuterBoundary.get(mOuterBoundary.size()-1-1), mOuterBoundary.get(0));
        // do all from 2nd to 2nd last segment
        for (int i = 1; i < mOuterBoundary.size()-1; ++i)
        {
        	mOuterBoundary.get(i).cleanup(mOuterBoundary.get(i-1), mOuterBoundary.get(i+1));
        }
        
        // do last segment
        mOuterBoundary.get(lastIndex).cleanup(mOuterBoundary.get(0));
        // do all from first to 2nd last segment
        for (int i = 0; i < mOuterBoundary.size()-1; ++i)
        {
        	mOuterBoundary.get(i).cleanup(mOuterBoundary.get(i+1));
        }
        /*
        for (int i = mOuterBoundary.size()-1; i >= 0; --i)
        {
        	mOuterBoundary.get(i).cleanup(mOuterBoundary, mRawTrack);
        }*/
        
        // TODO: do sanity check for validity (not to narrow etc) here
	}
	
	private boolean isPointInArea(RectF[] areas, PointF p)
	{
		for (int i = 0; i < areas.length; ++i)
		{
			if (p.x >= areas[i].left && p.x <= areas[i].right && p.y >= areas[i].top && p.y <= areas[i].bottom)
			{
				return true;
			}
		}
		return false;
	}
	
	private PointF calculateCenter()
	{
		float xc = 0, yc = 0;
		float area = 0;
		// first get all the points of the track (startpoints, controlpoints, last endpoint)
		float x[] = new float[mRawTrack.size()*2+1];
		float y[] = new float[mRawTrack.size()*2+1];
		int j = 0;
		for (int i = 0; i < mRawTrack.size(); ++i)
		{
			x[j] = mRawTrack.get(i).getStartPoint().x;
			y[j++] = mRawTrack.get(i).getStartPoint().y;
			x[j] = mRawTrack.get(i).getControlPoint().x;
			y[j++] = mRawTrack.get(i).getControlPoint().y;
			if (i == mRawTrack.size()-1)
			{
				x[j] = mRawTrack.get(i).getEndPoint().x;
				y[j] = mRawTrack.get(i).getEndPoint().y;
			}
		}
		// calculate the area of the polygon
		for (int i = 0; i < x.length-1; ++i)
		{
			area += (x[i] * y[i+1] - x[i+1] * y[i]); 
		}
		area *= 0.5;
		
		for (int i = 0; i < x.length-1; ++i)
		{
			xc += (x[i] + x[i+1]) * (x[i] * y[i+1] - x[i+1] * y[i]);
			yc += (y[i] + y[i+1]) * (x[i] * y[i+1] - x[i+1] * y[i]);
		}
		
		xc /= 6 * area;
		yc /= 6 * area;
		return new PointF(xc, yc);
	}
	
	
}
